/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.util.ArrayList;

class MathSpectralNorm {
    public static final int NUM_TWO_CONST = 2;
    public static final int NUM_LOOP_CONST = 10;
    public static final int NUM_WHILE_CONST = 48;
    public static final int NUM_STIME_CONST = 1000;
    public static final int NUM_TIME_LOOP_CONST = 80;

    public static double a(double i, double j) {
        double result = ((i + j) * (i + j + 1)) / NUM_TWO_CONST + i + 1;
        return 1.0 / result;
    }

    public static void au(ArrayList<Double> u, ArrayList<Double> v) {
        for (int i = 0; i < u.size(); ++i) {
            double t = 0;
            for (int j = 0; j < u.size(); ++j) {
                t += a(i, j) * u.get(j);
            }
            v.set(i, t);
        }
    }

    public static void atu(ArrayList<Double> u, ArrayList<Double> v) {
        for (int i = 0; i < u.size(); ++i) {
            double t = 0;
            for (int j = 0; j < u.size(); ++j) {
                t += a(j, i) * u.get(j);
            }
            v.set(i, t);
        }
    }

    public static void atAu(ArrayList<Double> u, ArrayList<Double> v, ArrayList<Double> w) {
        au(u, w);
        atu(w, v);
    }

    public static double spectralnorm(int n) {
        int i;
        ArrayList<Double> u = new ArrayList<>();
        ArrayList<Double> v = new ArrayList<>();
        ArrayList<Double> w = new ArrayList<>();


        double vv = 0;
        double vBv = 0;
        for (i = 0; i < n; ++i) {
            u.add(1.0);
            v.add(0.0);
            w.add(0.0);
        }
        for (i = 0; i < NUM_LOOP_CONST; ++i) {
            atAu(u, v, w);
            atAu(v, u, w);
        }
        for (i = 0; i < n; ++i) {
            vBv += u.get(i) * v.get(i);
            vv += v.get(i) * v.get(i);
        }
//        System.out.println("vBv = " + vBv + " , vv = " + vv);
        return Math.sqrt(vBv / vv);
    }
}

/*
 *  @State
 *  @Tags Jetstream2
 */
class Benchmark {
    /*
     *  @Benchmark
     */
    public static void run() {
        double total = 0;
        int n = 6;
        while (n <= MathSpectralNorm.NUM_WHILE_CONST) {
            total = total + MathSpectralNorm.spectralnorm(n);
            n = n * MathSpectralNorm.NUM_TWO_CONST;
        }
//        System.out.println("total = " + total);
        double expected = 5.086694231303284;
        if (total != expected) {
            System.out.println("ERROR: bad result: expected " + expected + "but got " + total);
        }
    }

    /**
     * @Benchmark
     */
    public static void runIterationTime() {
        double start = System.nanoTime() / 1000000.0;
        for (int i = 0; i < MathSpectralNorm.NUM_TIME_LOOP_CONST; i++) {
            run();
        }
        double end = System.nanoTime() / 1000000.0;
        System.out.println("math-spectral-norm: ms = " + (end - start));
    }

    public static void main(String[] args) {
        runIterationTime();
    }
}